/*
 * Wazuh Vulnerability Scanner - Unit Tests
 * Copyright (C) 2015, Wazuh Inc.
 * September 21, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "resultIndexer_test.hpp"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_schema.h"
#include "../scanOrchestrator/resultIndexer.hpp"
#include "../scanOrchestrator/scanContext.hpp"
#include "MockIndexerConnector.hpp"
#include "MockOsDataCache.hpp"
#include "TrampolineIndexerConnector.hpp"
#include "TrampolineOsDataCache.hpp"
#include "TrampolineRemediationDataCache.hpp"
#include "flatbuffers/flatbuffer_builder.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "json.hpp"

using ::testing::_;

namespace NSResultIndexerTest
{
    const std::string DELTA_PACKAGES_INSERTED_MSG =
        R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "dbsync_packages",
                "data": {
                    "architecture": "amd64",
                    "checksum": "1e6ce14f97f57d1bbd46ff8e5d3e133171a1bbce",
                    "description": "library for GIF images library",
                    "format": "deb",
                    "groups": "libs",
                    "item_id": "ec465b7eb5fa011a336e95614072e4c7f1a65a53",
                    "multiarch": "same",
                    "name": "libgif7",
                    "priority": "optional",
                    "scan_time": "2023/08/04 19:56:11",
                    "size": 72,
                    "source": "giflib",
                    "vendor": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
                    "version": "5.1.9-1",
                    "install_time": "1577890801"
                },
                "operation": "INSERTED"
            }
        )";

    const std::string CVEID {"CVE-2024-1234"};

    // Helpers

    const Os osData {.hostName = "osdata_hostname",
                     .architecture = "osdata_architecture",
                     .name = "osdata_name",
                     .codeName = "osdata_codeName",
                     .majorVersion = "osdata_majorVersion",
                     .minorVersion = "osdata_minorVersion",
                     .patch = "osdata_patch",
                     .build = "osdata_build",
                     .platform = "osdata_platform",
                     .version = "osdata_version",
                     .release = "osdata_release",
                     .displayVersion = "osdata_displayVersion",
                     .sysName = "osdata_sysName",
                     .kernelVersion = "osdata_kernelVersion",
                     .kernelRelease = "osdata_kernelRelease"};

    void expectOsData()
    {
        spOsDataCacheMock = std::make_shared<MockOsDataCache>();
        EXPECT_CALL(*spOsDataCacheMock, getOsData(_, _))
            .WillOnce(testing::Invoke(
                [](const std::string&, Os& osDataResult)
                {
                    osDataResult = osData;
                    return true;
                }));
    }
} // namespace NSResultIndexerTest

using namespace NSResultIndexerTest;

std::shared_ptr<MockIndexerConnector> spIndexerConnectorMock;

void ResultIndexerTest::SetUp() {}

void ResultIndexerTest::TearDown()
{
    spIndexerConnectorMock.reset();
    spOsDataCacheMock.reset();
    spRemediationDataCacheMock.reset();
}

/*
 * @brief Test handleRequest of the ResultIndexer class.
 */
TEST_F(ResultIndexerTest, TestHandleRequest)
{
    auto elementValue = nlohmann::json::parse(R"({"id": "id_test","no-index":false,"operation":"INSERTED"})");

    spIndexerConnectorMock = std::make_shared<MockIndexerConnector>();
    EXPECT_CALL(*spIndexerConnectorMock, publish(elementValue.dump())).Times(1);

    auto pIndexerConnectorTrap = std::make_shared<TrampolineIndexerConnector>();

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_PACKAGES_INSERTED_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContextOriginal =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContextOriginal->m_elements[CVEID] = elementValue; // Mock one vulnerability

    auto spResultIndexer = std::make_shared<
        TResultIndexer<TrampolineIndexerConnector,
                       TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>>(
        pIndexerConnectorTrap);

    EXPECT_NO_THROW(spResultIndexer->handleRequest(scanContextOriginal));
}

TEST_F(ResultIndexerTest, TestHandleRequestNoOperation)
{
    auto elementValue = nlohmann::json::parse(R"({"id": "id_test"})");

    spIndexerConnectorMock = std::make_shared<MockIndexerConnector>();
    EXPECT_CALL(*spIndexerConnectorMock, publish(elementValue.dump())).Times(0);

    auto pIndexerConnectorTrap = std::make_shared<TrampolineIndexerConnector>();

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_PACKAGES_INSERTED_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContextOriginal =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);
    scanContextOriginal->m_elements[CVEID] = elementValue; // Mock one vulnerability

    auto spResultIndexer = std::make_shared<
        TResultIndexer<TrampolineIndexerConnector,
                       TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>>(
        pIndexerConnectorTrap);

    EXPECT_NO_THROW(spResultIndexer->handleRequest(scanContextOriginal));
}

TEST_F(ResultIndexerTest, TestHandleRequestNoId)
{
    auto elementValue = nlohmann::json::parse(R"({"operation":"INSERTED"})");

    spIndexerConnectorMock = std::make_shared<MockIndexerConnector>();
    EXPECT_CALL(*spIndexerConnectorMock, publish(elementValue.dump())).Times(0);

    auto pIndexerConnectorTrap = std::make_shared<TrampolineIndexerConnector>();

    expectOsData();

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_PACKAGES_INSERTED_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const Synchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));
    auto scanContextOriginal =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorDelta);

    auto spResultIndexer = std::make_shared<
        TResultIndexer<TrampolineIndexerConnector,
                       TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>>(
        pIndexerConnectorTrap);

    EXPECT_NO_THROW(spResultIndexer->handleRequest(scanContextOriginal));
}
